{
 "nbformat": 4,
 "nbformat_minor": 2,
 "metadata": {
  "language_info": {
   "name": "python",
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   }
  },
  "orig_nbformat": 2,
  "file_extension": ".py",
  "mimetype": "text/x-python",
  "name": "python",
  "npconvert_exporter": "python",
  "pygments_lexer": "ipython3",
  "version": 3
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "贝叶斯介绍：\n",
    "+ 贝叶斯(Thomas Bayes，1701—1761)：英国牧师、业余数学家。<br>\n",
    "为了证明上帝的存在，他研究概率统计学原理。但生前没发表科学论著。\n",
    "+ 《 机遇理论问题中一个问题的解》 在其逝世2年后发表，开创了贝叶斯分析<br>\n",
    "的崭新统计思维斱式。但当时没受到重视（超时代，数学发展史中有很多类似的情况）。\n",
    "+ 20世纪中叶以后，由于经典统计遭遇困难(扔硬币)，逐渐进入全盛时期被发展为<br>\n",
    "一种关于统计推断的系统理论和方法，称为“ 贝叶斯方法”，由这种方法得到的统计<br>\n",
    "推断全部结果，称为“ 贝叶斯统计学”。\n",
    "+ 信奉贝叶斯统计，乃至鼓吹贝叶斯观点是统计推断唯一正确说<br>\n",
    "法的学者，形成数理统计学中的贝叶斯学派（Bayesian school）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "+ 总体信息：当前总体样本符合某种分布。比如抛硬币，二项分布。学生的某一科的成绩符合正态分布。\n",
    "+ 样本信息：通过抽样得到的部分样本的某种分布。\n",
    "+ 抽样信息=总体信息+样本信息\n",
    "+ 基于抽样信息进行统计推断的理论和方法称为经典统计学。\n",
    "+ 先验信息：抽样之前，有关推断问题中未知参数的一些信息，\n",
    "通常来自于经验或历史资料。\n",
    "+ 基于总体信息+样本信息+先验信息进行统计推断的方法和理\n",
    "论，称为贝叶斯统计学。\n",
    "+ 贝叶斯公式：$P(H|X) = \\frac{P(X|H)P(H)}{P(X)}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import fetch_20newsgroups\n",
    "from sklearn.model_selection import train_test_split"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "news = fetch_20newsgroups(subset='all')\n",
    "print(news.target_names)\n",
    "print(len(news.data))\n",
    "print(len(news.target))\n",
    "print(len(news.target_names))\n",
    "print(news.target[0])\n",
    "print(news.target_names[news.target[0]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train,x_test,y_train,y_test = train_test_split(news.data,news.target)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.feature_extraction.text import CountVectorizer\n",
    "\n",
    "texts=[\"dog cat fish\",\"dog cat cat\",\"fish bird\", 'bird']\n",
    "cv = CountVectorizer()\n",
    "cv_fit=cv.fit_transform(texts)\n",
    "\n",
    "# \n",
    "print(cv.get_feature_names())\n",
    "print(cv_fit.toarray())\n",
    "\n",
    "print(cv_fit.toarray().sum(axis=0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn import model_selection \n",
    "from sklearn.naive_bayes import MultinomialNB\n",
    "\n",
    "cv = CountVectorizer()\n",
    "cv_data = cv.fit_transform(x_train)\n",
    "mul_nb = MultinomialNB()\n",
    "\n",
    "scores = model_selection.cross_val_score(mul_nb, cv_data, y_train, cv=3, scoring='accuracy')  \n",
    "print(\"Accuracy: %0.3f\" % (scores.mean())) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TfidfVectorizer使用了一个高级的计算方法，称为Term Frequency Inverse Document  \n",
    "Frequency (TF-IDF)。这是一个衡量一个词在文本或语料中重要性的统计方法。直觉上讲，该方法通过比较在整个语料库的词的频率，寻求在当前文档中频率较高的词。这是一种将结果进行标准化的方法，可以避免因为有些词出现太过频繁而对一个实例的特征化作用不大的情况(我猜测比如a和and在英语中出现的频率比较高，但是它们对于表征一个文本的作用没有什么作用)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "# 文本文档列表\n",
    "text = [\"The quick brown fox jumped over the lazy dog.\",\n",
    "\"The dog.\",\n",
    "\"The fox\"]\n",
    "# 创建变换函数\n",
    "vectorizer = TfidfVectorizer()\n",
    "# 词条化以及创建词汇表\n",
    "vectorizer.fit(text)\n",
    "# 总结\n",
    "print(vectorizer.vocabulary_)\n",
    "print(vectorizer.idf_)\n",
    "# 编码文档\n",
    "vector = vectorizer.transform([text[0]])\n",
    "# 总结编码文档\n",
    "print(vector.shape)\n",
    "print(vector.toarray())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建变换函数\n",
    "vectorizer = TfidfVectorizer()\n",
    "# 词条化以及创建词汇表\n",
    "tfidf_train = vectorizer.fit_transform(x_train)\n",
    "\n",
    "scores = model_selection.cross_val_score(mul_nb, tfidf_train, y_train, cv=3, scoring='accuracy') \n",
    "print(\"Accuracy: %0.3f\" % (scores.mean()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_stop_words():\n",
    "    result = set()\n",
    "    for line in open('stopwords_en.txt', 'r').readlines():\n",
    "        result.add(line.strip())\n",
    "    return result\n",
    "\n",
    "# 加载停用词\n",
    "stop_words = get_stop_words()\n",
    "# 创建变换函数\n",
    "vectorizer = TfidfVectorizer(stop_words=stop_words)\n",
    "\n",
    "\n",
    "mul_nb = MultinomialNB(alpha=0.01)\n",
    "\n",
    "# 词条化以及创建词汇表\n",
    "tfidf_train = vectorizer.fit_transform(x_train)\n",
    "\n",
    "scores = model_selection.cross_val_score(mul_nb, tfidf_train, y_train, cv=3, scoring='accuracy') \n",
    "print(\"Accuracy: %0.3f\" % (scores.mean())) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 切分数据集\n",
    "tfidf_data = vectorizer.fit_transform(news.data)\n",
    "x_train,x_test,y_train,y_test = train_test_split(tfidf_data,news.target)\n",
    "\n",
    "mul_nb.fit(x_train,y_train)\n",
    "print(mul_nb.score(x_train, y_train))\n",
    "\n",
    "print(mul_nb.score(x_test, y_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn import datasets\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import classification_report,confusion_matrix\n",
    "from sklearn.naive_bayes import MultinomialNB,BernoulliNB,GaussianNB"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 载入数据\n",
    "iris = datasets.load_iris()\n",
    "x_train,x_test,y_train,y_test = train_test_split(iris.data, iris.target) \n",
    "mul_nb = GaussianNB()\n",
    "mul_nb.fit(x_train,y_train)\n",
    "print(classification_report(mul_nb.predict(x_test),y_test))\n",
    "print(confusion_matrix(mul_nb.predict(x_test),y_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "### 拼写检查器原理 ###\n",
    "在所有正确的拼写词中, 我们想要找一个正确的词 c, 使得对于 w 的条件概率最大。求解：  \n",
    "P(c|w) ->  P(w|c) P(c) / P(w)   \n",
    "比如：appla是条件w，apple和apply是正确的词c，对于apple和apply来说P(w)都是一样的，所以我们在上式中忽略它, 写成:    \n",
    "P(w|c) P(c)\n",
    "* P(c), 文章中出现这个正确拼写的词 c 的概率, 也就是说, 在英语文章中, c 出现的概率有多大。  \n",
    "假设可以认为单词在文章中出现的概率越大，则正确拼写的概率就越大，可以用单词出现次数来代替这个量。好比说, 英语中出现 the 的概率  P('the') 就相对高, 而出现  P('zxzxzxzyy') 的概率接近0(假设后者也是一个词的话).\n",
    "* P(w|c), 在用户想键入 c 的情况下敲成 w 的概率。这个是代表用户会以多大的概率把 c 敲错成 w。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import re"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "text = open('big.txt').read()# 读取内容\n",
    "text = re.findall('[a-z]+', text.lower())# 转小写，只保留a-z字符\n",
    "for t in text:\n",
    "    dic_words[t] = dic_words.get(t,0) + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  编辑距离: ###\n",
    "两个词之间的编辑距离定义为使用了几次插入(在词中插入一个单字母), 删除(删除一个单字母), 交换(交换相邻两个字母), 替换(把一个字母换成另一个)的操作从一个词变到另一个词."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 字母表\n",
    "alphabet = 'abcdefghijklmnopqrstuvwxyz'\n",
    "\n",
    "#返回所有与单词 word 编辑距离为 1 的集合\n",
    "def edits1(word):\n",
    "    n = len(word)\n",
    "    return set([word[0:i]+word[i+1:] for i in range(n)] +                     # deletion\n",
    "               [word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)] + # transposition\n",
    "               [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet] + # alteration\n",
    "               [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet])  # insertion"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#返回所有与单词 word 编辑距离为 2 的集合\n",
    "#在这些编辑距离小于2的词中间, 只把那些正确的词作为候选词\n",
    "def edits2(word):\n",
    "    return set(e2 for e1 in edits1(word) for e2 in edits1(e1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "P(w|c)求解：正常来说把一个元音拼成另一个的概率要大于辅音 (因为人常常把 hello 打成 hallo 这样); 把单词的第一个字母拼错的概率会相对小, 等等。但是为了简单起见, 选择了一个简单的方法: 编辑距离为1的正确单词比编辑距离为2的优先级高, 而编辑距离为0的正确单词优先级比编辑距离为1的高.一般把hello打成hallo的可能性比把hello打成halo的可能性大。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def known(words):\n",
    "    w = set()\n",
    "    for word in words:\n",
    "        if word in dic_words:\n",
    "            w.add(word)\n",
    "    return w\n",
    "\n",
    "# 先计算编辑距离，再根据编辑距离找到最匹配的单词\n",
    "def correct(word):\n",
    "    # 获取候选单词\n",
    "    #如果known(set)非空, candidates 就会选取这个集合, 而不继续计算后面的\n",
    "    candidates = known([word]) or known(edits1(word)) or known(edits2(word)) or word\n",
    "    # 字典中不存在相近的词\n",
    "    if word == candidates:\n",
    "        return word\n",
    "    # 返回频率最高的词\n",
    "    max_num = 0\n",
    "    for c in candcidates:\n",
    "        if dic_words[c] >= max_num:\n",
    "            max_num = dic_words[c]\n",
    "            candidate = c\n",
    "    return candidate\n",
    "\n",
    "correct('smoothig') # test"
   ]
  }
 ]
}